﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Reflection;

namespace Server {
	public class Server {
		private static volatile Server instance;
		protected static volatile Object syncRoot = new Object();
		private volatile Dictionary<Guid, ServerNodeControl> nodes;

		// An event that clients can use to be notified whenever the
		// elements of the list change.
		public event EventHandler NodeListChanged;

		// Invoke the Changed event; called whenever list changes
		protected virtual void OnNodeListChanged(EventArgs e) {
			if (NodeListChanged != null) {
				NodeListChanged(this, e);
			}
		}

		public Object SyncRoot {
			get {
				return syncRoot;
			}
		}

		private Server() {
			nodes = new Dictionary<Guid, ServerNodeControl>();
		}

		public Node AddNode(Guid guid, ServiceModelServer service) {
			ServerNodeControl nodeControl = new ServerNodeControl(guid, service);

			try {
				nodes.Add(guid, nodeControl);
				Logger.Log("Node " + guid.ToString() + " has been added");
				OnNodeListChanged(EventArgs.Empty);
			} catch (NullReferenceException) {
				return null;
			}

			return nodeControl.Node;
		}

		public void RemoveNode(Guid guid) {
			lock (SyncRoot) {
				if (nodes.ContainsKey(guid)) {
					nodes.Remove(guid);
					Logger.Log("Node " + guid.ToString() + " has been removed");
				}
			}
			OnNodeListChanged(EventArgs.Empty);
		}

		public ServerNodeControl NodeControl(Guid guid) {
			lock (SyncRoot) {
				if (!nodes.ContainsKey(guid)) {
					throw new IndexOutOfRangeException();
				}
				try {
					return nodes[guid];
				} catch (TargetInvocationException) {
					RemoveNode(guid);
					Logger.Warning(String.Format("TargetInvocationException on node {0}, removing...", guid));
					throw new IndexOutOfRangeException();
				}
			}
		}

		public Guid[] GetList() {
			lock (SyncRoot) {
				Guid[] guids = new Guid[nodes.Keys.Count];
				nodes.Keys.CopyTo(guids, 0);

				return guids;
			}
		}

		public static Server Instance {
			get {
				if (instance == null) {
					lock (syncRoot) {
						if (instance == null)
							instance = new Server();
					}
				}
				return instance;
			}
		}

		public Node GetNode(Guid guid) {
			return NodeControl(guid).Node;
		}
	}
}
